/*
 * Copyright 2014 JootMir Project
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * Support: http://www.jootmir.org
 */
package org.jootmir.client.core;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.apache.log4j.Logger;
import org.jootmir.client.core.texture.Texture;
import org.jootmir.client.core.texture.loader.TextureLoader;
import org.jootmir.client.core.texture.loader.TextureLoaderHandler;

import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
import com.googlecode.concurrentlinkedhashmap.EvictionListener;

/**
 * 进程资源缓存
 * <br> 
 * 此类实例进程唯一，并线程安全
 * <br>
 * 此类用于资源加载和缓存，加载为自动过程，不需要人为干预
 * <br>
 * 只需要告知本类实例加载资源即可
 * 
 * @author ShawRyan
 */
public final class AllCache {
	/** log */
	private static final Logger log = Logger.getLogger(AllCache.class);

	/* 单例 */
	private static final AllCache single = new AllCache();
	private AllCache() { }
	/** 获取进程唯一实例 */
	public static AllCache get() {
		return single;
	}
	
	/**
	 * 图片缓存数量
	 * <br>
	 * 对于在25*22可见性地图区域内，最多需要1100张背景图
	 * <br>
	 * 加上角色图片及武器图片
	 * <br>
	 * 最低需要同时存储在内存中的图片数量为3000左右
	 */
	private static final long MAX_IMAGE_CACHE_NUMBER = 3000;
	/**
	 * 图片信息缓存数量
	 * <br>
	 * 图片信息为图片本身信息与图片在库中位置及大小信息
	 * <br>
	 * 在保证内存优化的前提下可以将图片信息缓存在内存，必要时立时加载图片像素信息到内存
	 */
	private static final long MAX_TEXTURELOADERHANDLER_CACHE_NUMBER = 50000;
	
	/** 程序缓存的图片 */
	private ConcurrentLinkedHashMap<Integer, Texture> cachedTextures = new ConcurrentLinkedHashMap.Builder<Integer, Texture>()
			.maximumWeightedCapacity(MAX_IMAGE_CACHE_NUMBER)
			.listener(new EvictionListener<Integer, Texture>() {
				@Override
				public void onEviction(Integer arg0, Texture arg1) {
					arg1.release();
				}
			})
			.build();
	/** 程序缓存的纹理加载器 */
	private ConcurrentLinkedHashMap<Integer, TextureLoaderHandler> cachedTextureLoaderHandlers = new ConcurrentLinkedHashMap.Builder<Integer, TextureLoaderHandler>()
			.maximumWeightedCapacity(MAX_TEXTURELOADERHANDLER_CACHE_NUMBER)
			.build();
	
	/** 最大缓存文件指针数(针对资源文件)<br>对于操作系统而言，进程最多打开512个文件(最渣的系统吧-_-)，我们减少几个以免文件读取数超出 */
	private static final long MAX_FILE_POINTER = 500;
	/** 文件指针缓存 */
	private ConcurrentLinkedHashMap<String, RandomAccessFile> cachedFilePointers = new ConcurrentLinkedHashMap.Builder<String, RandomAccessFile>()
			.maximumWeightedCapacity(MAX_FILE_POINTER)
			.listener(new EvictionListener<String, RandomAccessFile>() {
				
				@Override
				public void onEviction(String arg0, RandomAccessFile arg1) {
					try {
						arg1.close();
					} catch (IOException e) {
						log.warn("流关闭异常！", e);
					}
				}
			})
			.build();
	/* 纹理加载相关 */
	/** 纹理加载任务队列 */
	private Set<TextureLoader> textureLoaders = new HashSet<TextureLoader>();
	/** 纹理加载任务 */
	public void putTextureLoader(TextureLoader textureLoader) {
		//if(textureLoaders.contains(textureLoader)) return;
		textureLoaders.add(textureLoader);
	}
	/** 更新加载队列 */
	void updateLoaders() {
		// 加载纹理
		Iterator<TextureLoader> itTexLoader = textureLoaders.iterator();
		while(itTexLoader.hasNext()) {
			TextureLoader texLoader = itTexLoader.next();
			if(texLoader.update()) {
				itTexLoader.remove();
			}
		}
		// 加载其他
	}
	
	/**
	 * 获取纹理
	 * <br>
	 * 此方法线程安全，其线程同步与互斥由{@link com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap ConcurrentLinkedHashMap}维护
	 * <br>
	 * 此方法可能返回null
	 * 
	 * @param index
	 * 		纹理索引
	 * @return 纹理或null
	 */
	public Texture getTexture(Integer index) {
		return cachedTextures.get(index);
	}
	/**
	 * 获取纹理加载器
	 * <br>
	 * 此方法线程安全，其线程同步与互斥由{@link com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap ConcurrentLinkedHashMap}维护
	 * <br>
	 * 此方法可能返回null
	 * 
	 * @param index
	 * 		纹理索引
	 * @return 加载器或null
	 */
	public TextureLoaderHandler getTextureLoaderHandler(Integer index) {
		return cachedTextureLoaderHandlers.get(index);
	}
	
	/**
	 * 存放纹理
	 * 
	 * @param index
	 * 		纹理索引
	 * @param texture
	 * 		纹理
	 */
	public void putTexture(Integer index, Texture texture) {
		cachedTextures.put(index, texture);
	}
	/**
	 * 存放纹理加载处理器
	 * 
	 * @param index
	 * 		纹理索引
	 * @param tlh
	 * 		纹理加载器
	 */
	public void putTextureLoaderHandler(Integer index, TextureLoaderHandler tlh) {
		cachedTextureLoaderHandlers.put(index, tlh);
	}
	
	/**
	 * 获取文件指针
	 * <br>
	 * 此方法线程安全，其线程同步与互斥由{@link com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap ConcurrentLinkedHashMap}维护
	 * <br>
	 * 此方法可能返回null
	 * <br>
	 * <b>对于文件流而言，可能文件指针不指向0，请在使用前自行将其归零</b>
	 * 
	 * @deprecated 请使用{@link Files#randomAccessFile(String) Files}代替
	 * @param path
	 * 		文件路径，值得一提的是同一个文件的不同路径表示不行:(
	 * @return
	 * 		文件随机读取器
	 */
	public RandomAccessFile getRandomAccessFile(String path) {
		return cachedFilePointers.get(path);
	}
	/**
	 * 存放文件指针
	 * 
	 * @param path
	 * 		文件路径
	 * @param raf
	 * 		文件随机读取器
	 */
	public void putRandomAccessFile(String path, RandomAccessFile raf) {
		cachedFilePointers.put(path, raf);
	}
}